﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Threading;
using RayDen.Library.Components;
using RayDen.Library.Components.Volume;
using RayDen.Library.Core;
using RayDen.Library.Core.Primitives;
using RayDen.Library.Data;
using RayDen.Library.Data.Import.Loader3DS;
using RayDen.Library.Data.Import.OBJLoader;
using RayDen.Library.Entity;
using RayDen.Library.Entity.Frames;
using RayDen.Library.Entity.Interface;
using RayDen.Library.Entity.Scene;
using RayDen.Library.Entity.SceneGraph;
using RayDen.SmallPT.Light;
using Point = RayDen.Library.Core.Primitives.Point;
using TriangleMesh = RayDen.Library.Components.TriangleMesh;

//using TriangleMesh = RayDen.RayEngine.Data.TriangleMesh;

namespace RayDen.SmallPT
{
    public class BaseRenderer : ConfigurableEntity, IRenderer
    {
        protected CancellationTokenSource cancelToken = new CancellationTokenSource();

        protected internal MediumInfo Air = new MediumInfo() { IoR = 1.000277f, Attenuation = 0.1f };
        protected internal MediumInfo Glass = new MediumInfo() { IoR = 1.46f, Attenuation = 0.05f };


        
        protected string stateString;

        protected internal int width;
        protected internal int height;

        protected internal FrameDescription frame;
        protected internal SceneGeometryInfo scene;
        protected internal List<ILight> lights;
        protected internal ICamera camera;
        protected internal DensityRegion sceneVolume;
        protected internal SceneManager manager;
        protected internal BsdfLibrary matLib;
        protected internal List<TriangleMesh> meshes;
        protected internal IMaterialProvider MaterialProvider;
        protected MaterialInfo[] mats;
        //protected readonly Dictionary<Type, Action<IFrameElement>> frameParsers;

        protected ThreadLocal<FastRandom> rnd;
        protected internal InfiniteLight EnvironmentMap;

        protected RenderStats stats;

        public int CpuThreadsUsed { get; set; }
        public int SamplesPerPixel
        {
            get { return samplesPerPixel; }
            set { samplesPerPixel = value; }
        }
        public float RayThreshold
        {
            get { return this.rayThreshold; }
            set { this.rayThreshold = value; }
        }
        public int MaxRecursionLevel
        {
            get { return this.maxRecursionLevel; }
            set { this.maxRecursionLevel = value; }
        }
        public int ShadowRayCount
        {
            get { return shadowRayCount; }
            set { shadowRayCount = value; }
        }
        public int samplesPerPixel = 4;

        protected float rayThreshold = 0.0001f;
        protected int maxRecursionLevel = 5;
        protected int shadowRayCount = 1;

        public class AreaLightInfo
        {
            public string GeoName;
            public RgbSpectrum Gain;
        }

        protected internal List<AreaLightInfo> areaLights;
        private SceneBuilder sceneBuilder;


        private List<RayDen.SmallPT.Light.TriangleLight> triLights;

        public bool IsLight(Triangle tri)
        {
            for (int index = 0; index < areaLights.Count; index++)
            {
                if (areaLights[index].GeoName.Equals(tri.Owner.MeshName))
                    return true;
            }
            return false;
        }

        public MeshLight GetLight(TriangleMesh tr)
        {
            return this.lights.Where(item=>item is MeshLight).Cast<MeshLight>().LastOrDefault(item => item.MeshName.Equals(tr.MeshName, StringComparison.InvariantCultureIgnoreCase));
        }

        public BaseRenderer()
        {
            CpuThreadsUsed = 8;
            stats = new RenderStats();
            lights = new List<ILight>();
            rnd = new ThreadLocal<FastRandom>(() => new FastRandom(((int)(DateTime.UtcNow.Ticks >> 16))));
            //frameParsers = new Dictionary<Type, Action<IFrameElement>> {
            //    { typeof (FrameCamera), 
            //        item => {
            //                    var cameraItem =(FrameCamera) item;
            //                    camera = this.CreateCamera(width, height, cameraItem);
                                   
            //        }},
            //    { typeof (FrameObjFileReference), 
            //        item => {
            //                    var objFileRef = (FrameObjFileReference)item;
            //                    var loader = new GlobalIndexObjLoader();
            //                    scene = loader.Load(objFileRef.ObjFilePath);
            //                    var mload = new MaterialLoader();
            //                    mats = mload.LoadMaterials(objFileRef.MtlFilePath);
            //                    foreach (
            //                        var materialInfo in mats) {
                                                                                                              
            //                            if (materialInfo.Name.ToLower().Contains("glass") ||
            //                                materialInfo.Name.ToLower().Contains("wire_134006006") 
            //                                ) {
            //                                    materialInfo.Kt = materialInfo.Kd;
            //                                    materialInfo.Kd = RgbSpectrum.ZeroSpectrum();

            //                                    materialInfo.MediumInfo = Glass;
            //                                }
            //                            if (materialInfo.Name.ToLower().Contains("metal")) {
            //                                materialInfo.Kr = materialInfo.Kd;
            //                            }
            //                        }
            //        }},


            //    { typeof(Frame3DsFileReference), item =>
            //        {
            //            var sceneFileRef = (Frame3DsFileReference)item;
            //            var loader = new SceneLoader();
            //            scene = loader.Load(sceneFileRef.FilePath);
            //            mats = loader.LoadMaterials(sceneFileRef.FilePath);

            //            foreach (var materialInfo in mats) {
            //                if (materialInfo.Name.ToLower().Contains("glass") ||
            //                    materialInfo.Name.ToLower().Contains("wire_134006006")
            //                    ) {
            //                        materialInfo.Kt = materialInfo.Kd;
            //                        materialInfo.Kd = RgbSpectrum.ZeroSpectrum();

            //                        materialInfo.MediumInfo = Glass;
            //                    }
            //                if (materialInfo.Name.ToLower().Contains("metal")) {
            //                    materialInfo.Kr = materialInfo.Kd;
            //                }
            //            }
            //            if (scene.Cameras != null && scene.Cameras.Any()) {
            //                camera = new PinholeCamera(width, height) {Fov  = scene.Cameras[0].Fov };
            //                camera.LookAt((Point) scene.Cameras[0].Position, (Point) scene.Cameras[0].Direction, -scene.Cameras[0].Up);
            //            }
            //        }},

            //    { typeof (FrameLightsource), 
            //        item => {
            //                    var lightsource = (FrameLightsource)item;
            //                    switch (lightsource.LightType) {
            //                        case LightSourceTypes.Point:
            //                            lights.Add(new PointLight(lightsource));
            //                            break;
            //                        case LightSourceTypes.EnvironmentMap:
            //                            lights.Add(EnvironmentMap = new InfiniteLight(lightsource));
            //                            break;
            //                        case LightSourceTypes.Area:
            //                            if (areaLights == null) {
            //                                areaLights = new List<AreaLightInfo>();
            //                            }
            //                            areaLights.Add(
            //                                new AreaLightInfo() {
            //                                    Gain = lightsource.Parameters.Get<RgbSpectrum>(FrameLightsource.LightsourcePower),
            //                                    GeoName = lightsource.Parameters.Get<string>(FrameLightsource.AreaLightGeometryName)
            //                                }
            //                                );
            //                            break;
            //                    }
            //        }}
            //};
        }

        //private ICamera CreateCamera(int width, int height, FrameCamera cameraItem)
        //{
        //    /*
        //    var camera = new PinholeCamera
        //                               (width,
        //                                height); { };
        //    camera.LookAt((Point)cameraItem.Position, (Point)cameraItem.Target, -cameraItem.Up);
        //    return camera;
        //    */
        //    return new BasicPerspectiveCamera((Point) cameraItem.Position, cameraItem.Target, -cameraItem.Up,
        //                                      width, height);
        //}

        #region IRenderer Members

        public virtual Object Properties
        {
            get { return frame; }
        }

        public virtual string Name
        {
            get { return this.GetType().Name; }
        }

        public int Width
        {
            get { return width; }
            set { width = value; }
        }

        public int Height
        {
            get { return height; }
            set { height = value; }
        }

        public virtual void Setup(IRayDenLibraryFrame newFrame)
        {
            var svc = new SceneGraphService();
            svc.Populate(new GlobalIndexObjLoader(), new MaterialLoader());
            sceneBuilder = new SceneBuilder() { SGService = svc };
            sceneBuilder.Setup(this);
            sceneBuilder.OpenFrame(newFrame);

        }

        public virtual void RenderPreview(Bitmap output, Action<int> progressCallback)
        {

        }

        public virtual void StartRendering(Action<IFilmFrame, int> taskCallback, Action<IFilmFrame> endTaskCallback)
        {

        }

        public virtual void StopRender()
        {
            this.cancelToken.Cancel();
        }

        public virtual IRendererStatistics GetRendererInfo()
        {
            return this.stats;
        }

        public virtual string GetCurrentStateInfo()
        {

            var mrs = (this.stats.TotalRays*0.000001)/this.stats.Elapsed.TotalSeconds;
            var mss = (this.stats.TotalSamples * 0.000001) / this.stats.Elapsed.TotalSeconds;
            return string.Format("Elapsed {0} MRays/sec {1:F6} MSamples/sec {2:F6}", this.stats.Elapsed, mrs, mss);

            //return string.Format("Elapsed {0} TotalRays {1} TotalSamples {2}", this.stats.Elapsed, this.stats.TotalRays,this.stats.TotalSamples);
            //return stateString;
        }

        #endregion

        #region IEnumerable Members

        public new System.Collections.IEnumerator GetEnumerator()
        {
            throw new NotImplementedException();
        }

        #endregion

        protected ILight GetLight(out float val)
        {
            val = (float)1f / lights.Count;
            return lights[rnd.Value.Next(0, lights.Count - 1)];
        }
        protected Triangle GetTriangle(int mi,int index)
        {
            return Triangles[index];
        }

        protected Triangle[] triangles;

        protected Triangle[] Triangles
        {
            get
            {
                if (triangles == null)
                {
                    triangles = meshes.SelectMany(item => item.triangles).ToArray();                    
                }
                return triangles;
            }
        }
    }
}